﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NTiering;
using NTiering.EventHandlers;
using NTiering.Models;
using NTiering.Search;

namespace NTiering.Examples
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Simple example , ui model in, data model out ");
            Example1();
            Console.WriteLine("Any key to continue");
            Console.ReadKey();

            Console.WriteLine("Validation example , ui model in, errors outputted ");
            Example2();
            Console.WriteLine("Any key to continue");
            Console.ReadKey();

            Console.WriteLine("Validation example , ui model in, data model out  ");
            Example3();
            Console.WriteLine("Any key to continue");
            Console.ReadKey();

            Console.WriteLine("Search example , search params in, single data model out  ");
            Example4();
            Console.WriteLine("Any key to continue");
            Console.ReadKey();

            Console.WriteLine("Search example , search params in, many data models out  ");
            Example5();
            Console.WriteLine("Any key to continue");
            Console.ReadKey();

        }
             
        
        private static void Example1()
        {            
            var service = new NTiering.Service(); // create a service 
            service.AddMapper<CustomerUIModel, CustomerDataModel>(); // tells the service there is a relationship between these models

            service.AddEventHandler(EventHandlerTriggers.OnTryCreatePass, OutputToConsole); // tell the service to send the output to "OutputToConsole"

            var input = new CustomerUIModel { Name = "Joe" };
            service.TryCreate<CustomerUIModel>(input, new List<string>());   
        }

        private static void Example2()
        {
            var service = new NTiering.Service(); // create a service 
            service.AddMapper<CustomerUIModel, CustomerDataModel>(); // tells the service there is a relationship between these models
            service.AddValidator<CustomerDataModel>((x, y) => { return x.Name + " is not allowed"; });// add a validator (for the data model) return numm or empty string to pass validation
            service.AddValidator<CustomerDataModel>((x, y) => { return x.Name + " is too short a name"; });// add a validator (for the data model) return numm or empty string to pass validation
            service.AddEventHandler(EventHandlerTriggers.OnValidationFail, OutputToConsole); // tell the service to send the output to "OutputToConsole"

            var input = new CustomerUIModel { Name = "Joe" };
            Console.WriteLine("Validation Errors");
            var errors = new List<string>() ;
            var didItWork = service.TryCreate<CustomerUIModel>(input, errors);
            /*
             didItWork is now false, and errors also contains a copy of the error messages from validation 
            */
        }

        private static void Example3()
        {
            var service = new NTiering.Service(); // create a service 
            service.AddMapper<CustomerUIModel, CustomerDataModel>(); // tells the service there is a relationship between these models
            service.AddValidator<CustomerDataModel>((x, y) => { return ""; });// add a validator (for the data model) return numm or empty string to pass validation
            service.AddValidator<CustomerDataModel>((x, y) => { return null; });// add a validator (for the data model) return numm or empty string to pass validation
            
            service.AddEventHandler(EventHandlerTriggers.OnValidationFail, OutputToConsole); // tell the service to send the output to "OutputToConsole"
            service.AddEventHandler(EventHandlerTriggers.OnTryCreatePass, OutputToConsole); // tell the service to send the output to "OutputToConsole"

            var input = new CustomerUIModel { Name = "Joe" };
            Console.WriteLine("No Validation Errors");
            service.TryCreate<CustomerUIModel>(input);
        }

        private static void Example4()
        {
            var service = new NTiering.Service(); // create a service 
            service.AddMapper<CustomerUIModel, CustomerDataModel>(); // tells the service there is a relationship between these models
            service.AddMapper<CustomerDataModel, CustomerUIModel>(); // tells the service there is a relationship between these models

            service.AddEventHandler(EventHandlerTriggers.OnSearchRequest, CompleteSearchRequest); // tell the service to send the output to "OutputToConsole"


            var searchContext = new CustomerNameSearchContext
            {
                SearchType = CustomerSeachType.SearchByCustomerName,
                Name = "James"
            };

            service.SearchFor<CustomerUIModel>(searchContext);
            OutputToConsole(searchContext);
        }

        private static void Example5()
        {
            var service = new NTiering.Service(); // create a service 
            service.AddMapper<CustomerUIModel, CustomerDataModel>(); // tells the service there is a relationship between these models
            service.AddMapper<CustomerDataModel, CustomerUIModel>(); // tells the service there is a relationship between these models

            service.AddEventHandler(EventHandlerTriggers.OnSearchRequest, CompleteSearchRequest); // tell the service to send the output to "OutputToConsole"


            var searchContext = new CustomerNameSearchStartsWithContext
            {
                SearchType = CustomerSeachType.SearchByCustomerNameStartsWith,
                Name = "J"
            };

            service.SearchFor<CustomerUIModel>(searchContext);
            OutputToConsole(searchContext);
        }

        private static void CompleteSearchRequest(IEventContext obj)
        {
            var searchContext = obj as CustomerNameSearchContext;
            if (searchContext != null)
            {
                /// seach -> SearchByCustomerName 
                if (searchContext.SearchType == CustomerSeachType.SearchByCustomerName)
                {
                    var datamodel = DummyCustomerDataContext().FirstOrDefault(x => x.Name == searchContext.Name);
                    if (datamodel != null)
                    {
                        // use MapToModel t ouse any busines rules the service has
                        searchContext.Result = searchContext.Service.MapToModel<CustomerDataModel>(datamodel) as CustomerUIModel;
                    }
                }
                
            }

            var searchContextStartsWith = obj as CustomerNameSearchStartsWithContext;
            if(searchContextStartsWith != null)
            {
            /// seach -> SearchByCustomerNameStartsWith 
                if (searchContextStartsWith.SearchType == CustomerSeachType.SearchByCustomerNameStartsWith)
                {
                    searchContextStartsWith.Results = new List<CustomerUIModel>();
                    var datamodels = DummyCustomerDataContext().Where(x => x.Name.StartsWith(searchContextStartsWith.Name));
                    foreach (var d in datamodels)
                    { 
                        searchContextStartsWith.Results.Add
                            (
                            searchContextStartsWith.Service.MapToModel<CustomerDataModel>(d) as CustomerUIModel
                            );
                    }                    
                }
            }            
        }

        /// <summary>
        /// outputs all the data to the console ...
        /// </summary>
        /// <param name="x"></param>
        private static void OutputToConsole(object x)
        {
            /// output 
            var createUpdateDeleteEventContext = x as CreateUpdateDeleteEventContext;
            if (createUpdateDeleteEventContext != null)
            {
                var o = createUpdateDeleteEventContext.SourceObject as CustomerDataModel;
                Console.WriteLine("Outputted " + o.GetType().Name + " Name:" + o.Name);
            }

            /// output 
            var validationFailEventContext = x as ValidationFailEventContext;
            if (validationFailEventContext != null)
            {
                foreach (var errorMsg in validationFailEventContext.Errors)
                {
                    Console.WriteLine("Error :" + errorMsg);
                }
            }

            /// output 
            var customerNameSearchContext = x as CustomerNameSearchContext;
            if (customerNameSearchContext != null)
            {
                Console.WriteLine("Found customer " + customerNameSearchContext.Name);
                Console.WriteLine(" and the search took " + customerNameSearchContext.Duration.Milliseconds + " milliseconds");
            }

            /// output 
            var customerNameSearchStartsWithContext = x as CustomerNameSearchStartsWithContext;
            if (customerNameSearchStartsWithContext != null)
            {
                Console.WriteLine("Found customers ");
                foreach (var c in customerNameSearchStartsWithContext.Results)
                {
                    Console.WriteLine(c.Name);
                }
                Console.WriteLine(" and the search took " + customerNameSearchStartsWithContext.Duration.Milliseconds + " milliseconds");
            }
 
        }

        private static IQueryable<CustomerDataModel> DummyCustomerDataContext()
        {
            var rtn = new List<CustomerDataModel>();
            rtn.Add(new CustomerDataModel { Name = "James" });
            rtn.Add(new CustomerDataModel { Name = "Joyce" });
            rtn.Add(new CustomerDataModel { Name = "Jan" });
            rtn.Add(new CustomerDataModel { Name = "Stephen" });
            return rtn.AsQueryable();        
        }
    }



    // ----  Example classes --- //
    enum CustomerSeachType
    {
        SearchByCustomerName ,
        SearchByCustomerNameStartsWith
       
    }
    class CustomerNameSearchContext : ISearchContext
    {
        public TimeSpan Duration { get; set; }
        public IService Service { get; set; }
        public object SourceObject { get; set; }
        public EventHandlerTriggers SourceTrigger { get; set; }
        public string Name { get; set; }
        public CustomerSeachType SearchType { get; set; }
        public CustomerUIModel Result { get; set; }
    }

    class CustomerNameSearchStartsWithContext : ISearchContext
    {
        public TimeSpan Duration { get; set; }
        public IService Service { get; set; }
        public object SourceObject { get; set; }
        public EventHandlerTriggers SourceTrigger { get; set; }
        public string Name { get; set; }
        public CustomerSeachType SearchType { get; set; }
        public List<CustomerUIModel> Results { get; set; }
    }


    class CustomerUIModel :IUiModel
    {
        public string Name { get; set; }
    }

    class CustomerDataModel :IDataModel 
    {
        public string Name { get; set; }
    }
}
